package val.to.server;

import java.io.IOException;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import org.apache.http.client.ClientProtocolException;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import val.to.ErrorHelper;
import val.to.HttpUtil;
import val.to.HttpUtil.HttpUtilResponse;
import val.to.config.Constants;
import val.to.entities.Event;
import val.to.entities.FeedFilterType;
import val.to.entities.FeedItem;
import val.to.entities.PhotoPost;
import android.location.Location;
import android.util.Log;

/**
 * RESTful ServerPortal impl.
 * @author Roman
 *
 */
public class RESTServerPortal implements IServerPortal {

	public static final String SERVER_URI = "http://valtoapp.appspot.com";
	//public static final String SERVER_URI  = "http://valtoappdev.appspot.com";
	
	private String mLastError;
	
	@Override
	public boolean postPhoto(PhotoPost photoPost, int targetEventId){

		JSONObject json;
		try {
			json = photoPost.json();
		} catch (JSONException e) {
			// TODO;
			return false;
		}

		try {
			HttpUtil.postRequest(SERVER_URI + "/event/" + targetEventId, json);
		} 
		catch (ClientProtocolException e) {
			handleException(e);
			return false;
		} catch (IOException e) {
			handleException(e);
			return false;
		}
		
		return true;
	}

	@Override
	public boolean postEvent(Event newEvent) {

		JSONObject json;
		try {
			json = newEvent.json();
		} catch (JSONException e) {
			//not supposed to happen at all.
			handleException(e);
			return false;
		}

		HttpUtilResponse resp;
		try {
			resp = HttpUtil.postRequest(SERVER_URI + "/events", json);
			
		}
		catch (ClientProtocolException e) {
			handleException(e);
			return false;
		}
		catch (IOException e) {
			handleException(e);
			return false;
		}
		
		if (!resp.success){
			mLastError = httpCodeToError(resp.code);
			return false;
		}
		
		JSONObject retJson;
		try {
			retJson = new JSONObject(resp.content);
			newEvent.finalizeEvent(retJson);
		} catch (JSONException e) {
			handleException(e);
			return false;
		}

		return true;
	}
	
	public boolean exportEvent(Event event) {
		HttpUtilResponse resp;
		try {
			resp = HttpUtil.postRequest(SERVER_URI + "/event/" + event.getId() + "/exportPhotos");
			
		}
		catch (ClientProtocolException e) {
			handleException(e);
			return false;
		}
		catch (IOException e) {
			handleException(e);
			return false;
		}
		
		if (!resp.success){
			mLastError = httpCodeToError(resp.code);
			return false;
		}

		return true;
	}
	
	@Override 
	public boolean deleteEvent(Event event)
	{
		HttpUtilResponse resp;
		try {
			resp = HttpUtil.deleteRequest(SERVER_URI + "/event/" + event.getId());
			
		}
		catch (ClientProtocolException e) {
			handleException(e);
			return false;
		}
		catch (IOException e) {
			handleException(e);
			return false;
		}
		
		if (!resp.success){
			mLastError = httpCodeToError(resp.code);
			return false;
		}

		return true;
	}
	
	@Override
	public boolean updateEvent(Event event)
	{
		JSONObject json;
		try {
			json = event.json();
		} catch (JSONException e) {
			//not supposed to happen at all.
			handleException(e);
			return false;
		}

		HttpUtilResponse resp;
		try {
			resp = HttpUtil.putRequest(SERVER_URI + "/event/" + event.getId(), json);
			
		}
		catch (ClientProtocolException e) {
			handleException(e);
			return false;
		}
		catch (IOException e) {
			handleException(e);
			return false;
		}
		
		if (!resp.success){
			mLastError = httpCodeToError(resp.code);
			return false;
		}

		return true;
	}

	@Override
	public ArrayList<Event> getEvents(Location location) {
		Map<String, String> args = new HashMap<String, String>();
		double lon = location.getLongitude();
		double lat = location.getLatitude();

		args.put("location_lon", Double.toString(lon));
		args.put("location_lat", Double.toString(lat));

		HttpUtilResponse resp;
		try {
			resp = HttpUtil.getRequest(SERVER_URI + "/events", args);
		} catch (ClientProtocolException e) {
			handleException(e);
			return null;
		}
		catch (IOException e) {
			handleException(e);
			return null;
		}

		if (!resp.success){
			mLastError = httpCodeToError(resp.code);
			return null;
		}
		
		ArrayList<Event> events = new ArrayList<Event>();

		try {
			String strArray = new JSONObject(resp.content).getString("data");
			JSONArray jsonArr = new JSONArray(strArray);
			for (int i = 0; i < jsonArr.length(); ++i) {
				events.add(Event.FromJson(jsonArr.getJSONObject((i))));
			}
		} catch (JSONException e) {
			handleException(e);
			return null;
		}

		return events;
	}
	
	@Override
	public ArrayList<Event> getOwnerEvents() {
		Map<String, String> args = new HashMap<String, String>();
		HttpUtilResponse resp;
		try {
			resp = HttpUtil.getRequest(SERVER_URI + "/userProfile", args);
		} catch (ClientProtocolException e) {
			handleException(e);
			return null;
		}
		catch (IOException e) {
			handleException(e);
			return null;
		}

		if (!resp.success){
			mLastError = httpCodeToError(resp.code);
			return null;
		}
		
		ArrayList<Event> events = new ArrayList<Event>();

		try {
			JSONObject response = new JSONObject(resp.content);
			JSONArray jsonArr = response.getJSONArray("eventsList");
	
			for (int i = 0; i < jsonArr.length(); ++i) {
				JSONObject eventJson = jsonArr.getJSONObject(i);
				if (eventJson.getBoolean("owner") == true)
					events.add(Event.FromJson(eventJson));
			}
		} catch (JSONException e) {
			handleException(e);
			return null;
		}

		return events;
	}
	
	@Override
	public FeedUpdate getFeedItems(int eventId, Date refDate, Date lastUpdate, int maxCount, boolean forward, FeedFilterType filter) {
		
		Map<String, String> args = new HashMap<String, String>();
		args.put("maximum_items", Integer.toString(maxCount));
		if (refDate != null){
			//TOOD: patch
			Date pDate = new Date(refDate.getTime() + 1000); //add 1 sec as a patch
			
			//Ref date based operation
			args.put("order", forward ? "after" : "before");
			args.put("from_date", JsonUtils.jsonDateFormatUtc(pDate));					
			args.put("lastUpdate", JsonUtils.jsonDateFormatUtc(lastUpdate));					
		}
		else{
			//Get the X most recent items.
			args.put("from_date", "0");		
			args.put("lastUpdate", "0");		
			args.put("order", "recent");
		}

		//provide the relevant filter
		args.put("fromUser", filter.toString());
		
		HttpUtilResponse resp;
		try {
			resp = HttpUtil.getRequest(SERVER_URI + "/event/" + eventId, args);
		} 
		catch (ClientProtocolException e) {
			handleException(e);
			return null;
		}
		catch (IOException e) {
			handleException(e);
			return null;
		}
		

		if (!resp.success){
			mLastError = httpCodeToError(resp.code);
			return null;
		}
		
		ArrayList<FeedItem> newItems = new ArrayList<FeedItem>();
		ArrayList<Integer> deletedItems = new ArrayList<Integer>();
		Date newLastUpdate = null;
		
		try {
			JSONObject response = new JSONObject(resp.content);
	
			JSONArray recentArr = response.getJSONArray("recentItems");
			JSONArray deletedArr = response.getJSONArray("deletedItems");

			for (int i = 0; i < recentArr.length(); ++i) {
				newItems.add(FeedItem.FromJson(recentArr.getJSONObject((i))));
			}	

			for (int i = 0; i < deletedArr.length(); ++i) {
				deletedItems.add(deletedArr.getInt(i));
			}
			
			String lastUpdateStr = response.getString("lastUpdate");		
			newLastUpdate = JsonUtils.dateFromJson(lastUpdateStr);
			
		} catch (JSONException e) {
			handleException(e);
			return null;
		}

		return new FeedUpdate(newItems, deletedItems, newLastUpdate);
	}
	
	@Override
	public ArrayList<String> getFriendsListCheckedInEvent(int eventId)
	{
		HttpUtilResponse resp;
		try {
			Map<String, String> args = new HashMap<String, String>();
			resp = HttpUtil.getRequest(SERVER_URI + "/event/" + eventId + "/friendsCheckedIn", args);
			
		}
		catch (ClientProtocolException e) {
			handleException(e);
			return null;
		}
		catch (IOException e) {
			handleException(e);
			return null;
		}
		
		if (!resp.success){
			mLastError = httpCodeToError(resp.code);
			return null;
		}
		
		ArrayList<String> friends = new ArrayList<String>();

		try {
			String strArray = new JSONObject(resp.content).getString("friendsCheckedInList");
			JSONArray jsonArr = new JSONArray(strArray);
			for (int i = 0; i < jsonArr.length(); ++i) {
				friends.add(jsonArr.getString(i));
			}
		} catch (JSONException e) {
			handleException(e);
			return null;
		}

		return friends;
	}
	
	@Override
	public boolean deletePhoto(int eventId, int photoId){
		try {
			String uri = String.format(SERVER_URI + "/event/%d/item/%d", eventId, photoId);
			HttpUtilResponse resp = HttpUtil.deleteRequest(uri);
			if (!resp.success){
				mLastError = httpCodeToError(resp.code);
			}
			
			return resp.success;
		}
		catch (ClientProtocolException e) {
			handleException(e);
			return false;
		} 
		catch (IOException e) {
			handleException(e);
			return false;
		} 
	}

	@Override
	public String getLastError() {
		return mLastError;
	}
	
	@Override
	public boolean checkIntoEvent(int eventId){
		HttpUtilResponse resp;
		try {
			String uri = String.format(SERVER_URI + "/event/%d/checkin", eventId);
			resp = HttpUtil.postRequest(uri, null);
			
		}
		catch (ClientProtocolException e) {
			handleException(e);
			return false;
		}
		catch (IOException e) {
			handleException(e);
			return false;
		}
		
		if (!resp.success){
			mLastError = httpCodeToError(resp.code);
			return false;
		}
		
		return true;
	}
	
	@Override
	public boolean shareEvent(int eventId){
		HttpUtilResponse resp;
		try {
			String uri = String.format(SERVER_URI + "/event/%d/share", eventId);
			resp = HttpUtil.postRequest(uri, null);
			
		}
		catch (ClientProtocolException e) {
			handleException(e);
			return false;
		}
		catch (IOException e) {
			handleException(e);
			return false;
		}
		
		if (!resp.success){
			mLastError = httpCodeToError(resp.code);
			return false;
		}
		
		return true;
	}

	private void handleException(Exception ex){
		if (ex instanceof UnknownHostException){
			//Connectivity exception.
			//TODO: return some proper error so the user will see a message 
			//such as "Yo buddy, you got internet problems!".
			mLastError = "Failed to connect to remote server, make sure the device is connected to the internet.";
			//TODO: move to resource
		}
		else{
			mLastError = ex.getMessage();
		}
		
		ErrorHelper.handleError(ex);
	}
	
	
	private static String httpCodeToError(int code) {
		Log.e(Constants.TAG, String.format("REST portal error: HTTP %d code was returned", code));
		
		switch (code) {
		
		case 401:
			return "Unauthorized. Facebook token might be expired.";
		
		case 403:
			return "Access forbidden. Facebook token might be expired.";
			
		case 500:
			return "Internal server error. Unkown cause.";
			
		case 400:
			return "Invalid request, some fields or arguments are missing or incorrect.";

		default:
			return "Unknown server side error.";
		}			
	}
}
